<?php
/* 
 * Copyright (c) 2011 Antoine d'Otreppe de Bouvette
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

abstract class Select {
    /**
     * @var array
     */
    private $projection;
    
    /**
     *
     * @var string
     */
    private $orderBy;
    
    /**
     *
     * @var string
     */
    private $table;
    
    /**
     *
     * @var Condition[]
     */
    private $conditions;
    
    /**
     *
     * @var string
     */
    private $groupBy;
    
    /**
     * The maximum number of results
     * 
     * @var int
     */
    private $max;
    
    /**
     * @var int The offset of the first result
     */
    private $offset;
    
    /**
     *
     * @var array
     */
    private $args;
    
    public function __construct() {
        $this->projection = array('*');
        $this->where = array();
        $this->args = array();
    }
    
    abstract public function __toString();
    
    protected function translateArgs($statement, array $args) {
        $final = '';
        
        $offset = 0;
        while(($next = strpos($statement, '?', $offset)) !== false) {
            $final .= substr($statement, $offset, $next - $offset);
            $final .= $this->argument(array_shift($args));
            $offset = $next + 1;
        }
        
        return $final . substr($statement, $offset);
    }
    
    protected function argument($value) {
        $argName = 'p'.count($this->args);
        $this->args[$argName] = $value;
        return ':'.$argName;
    }
    
    public function getProjection() {
        return $this->projection;
    }

    /**
     *
     * @param array $projection
     * @return Select 
     */
    public function columns(array $projection) {
        $this->projection = $projection;
        return $this;
    }

    public function getOrderBy() {
        return $this->orderBy;
    }

    /**
     *
     * @param string $orderBy 
     * @return Select
     */
    public function orderBy($orderBy) {
        $this->orderBy = $orderBy;
        return $this;
    }

    public function getTable() {
        return $this->table;
    }

    /**
     * @param string $table 
     * @return Select
     */
    public function from($table) {
        $this->table = $table;
        return $this;
    }

    public function getConditions() {
        return $this->conditions;
    }

    /**
     *
     * @param string $condition 
     * @param mixed... $varargs
     * @return Select
     */
    public function where($condition /*, ...$varags */) {
        $varargs = func_get_args();
        array_shift($varargs);
        $this->conditions[] = $this->translateArgs($condition, $varargs);
        
        return $this;
    }

    public function getGroupBy() {
        return $this->groupBy;
    }

    /**
     *
     * @param string $groupBy 
     * @return Select
     */
    public function groupBy($groupBy) {
        $this->groupBy = $groupBy;
        return $this;
    }

    public function getArgs() {
        return $this->args;
    }

    public function limit($max, $offset=0) {
        $this->max = (int) $max;
        $this->offset = (int) $offset;
        return $this;
    }
    
    public function getMax() {
        return $this->max;
    }

    public function getOffset() {
        return $this->offset;
    }
}
